package cn.edu.zzuli.nothinglink.service.impl;

import cn.edu.zzuli.nothinglink.constant.LiveConst;
import cn.edu.zzuli.nothinglink.constant.ResCodeConst;
import cn.edu.zzuli.nothinglink.entity.*;
import cn.edu.zzuli.nothinglink.mapper.LiveInfoMapper;
import cn.edu.zzuli.nothinglink.mapper.LiveRecordMapper;
import cn.edu.zzuli.nothinglink.mapper.ProblemMapper;
import cn.edu.zzuli.nothinglink.mapper.StudentsMapper;
import cn.edu.zzuli.nothinglink.service.ClassesService;
import cn.edu.zzuli.nothinglink.service.ProblemService;
import cn.edu.zzuli.nothinglink.vo.LiveProblemInfoVo;
import cn.edu.zzuli.nothinglink.vo.ProblemClassInfoVo;
import cn.edu.zzuli.nothinglink.vo.ProblemRecordVo;
import cn.edu.zzuli.nothinglink.ws.LiveLinkEndPoint;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author suzyyy
 * @since 2021-04-08
 */
@Service
public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> implements ProblemService {

    @Resource
    LiveRecordMapper liveRecordMapper;

    @Resource
    ProblemMapper problemMapper;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Resource
    LiveInfoMapper liveInfoMapper;

    @Autowired
    ClassesService classesService;

    @Resource
    StudentsMapper studentsMapper;

    @Override
    public R publishProblem2TheStuClients(String stream, JSONObject json) {
        String content = json.getString("content");
        List<Object> checkInfos = json.getJSONArray("checkInfos");

        //获取标志位，此时不可以发布问题。
        if (redisTemplate.hasKey(LiveConst.LIVE_CAN_PUBLISH_PROBLEM.KEY() + stream)) {
            return R.error().add("error", "两分钟内只能发送一次问题，给学生点思考空间哦 :)");
        }

        String problemUuid = UUID.randomUUID().toString().substring(0, 8);

        LiveProblemInfoVo liveProblemInfoVo
                = new LiveProblemInfoVo(content, stream, problemUuid, checkInfos);

        try {

            //创建一条问题记录。修改直播提问次数。
            CompletableFuture<Boolean> saveTask = CompletableFuture.supplyAsync(() -> {
                BoundHashOperations<String, Object, Object> ops = redisTemplate
                        .boundHashOps(LiveConst.LIVE_START_INFO.KEY() + stream);
                Integer totalNumber = (Integer) ops.get(LiveConst.LIVE_TOTAL_NUMBER.KEY());
                return saveDate2db(stream, problemUuid, json.toJSONString(), totalNumber);
            });

            //跟签到一样。设置一个 hash，存放学生的提交记录。然后在结束直播的时候，再去保存签到和提交记录。
            BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(
                    LiveConst.LIVE_PROBLEM.KEY() + stream + ":" + problemUuid);
            ops.put("0", new ProblemDetail());

            redisTemplate.opsForValue().set(LiveConst.LIVE_CAN_PUBLISH_PROBLEM.KEY() + stream,false, 2, TimeUnit.MINUTES );

            //通知学生端回答
            LiveLinkEndPoint.broadCast2AllStuInTheLive(
                    R.success().setCode(ResCodeConst.PROBLEM.code())
                            .add("problemInfo", liveProblemInfoVo),
                    stream
            );

            if (saveTask.get()) return R.success();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        return R.error().add("error", "后台有点错误...");
    }

    @Override
    public boolean saveStuProblemData(R msg) {
        String check = null, uuid = null, stream = null, stuName = null, stuNumber = null, className = null;
        Integer stuId = null;

        if ((check = (String) msg.getData().get("check")) == null
                || (uuid = (String) msg.getData().get("uuid")) == null
                || (stream = (String) msg.getData().get("stream")) == null
                || (stuName = (String) msg.getData().get("stuName")) == null
                || (stuNumber = (String) msg.getData().get("stuNumber")) == null
                || (className = (String) msg.getData().get("className")) == null
                || (stuId = (Integer) msg.getData().get("stuId")) == null) {
            return false;
        }

        ProblemDetail problemDetail = new ProblemDetail()
                .setPdReplyTime(LocalDateTime.now())
                .setStuNumber(stuNumber)
                .setClassName(className)
                .setPdReply(check)
                .setPdStuId(stuId).setPdStuName(stuName);

        try {
            if (redisTemplate.hasKey(LiveConst.LIVE_PROBLEM.KEY() + stream + ":" + uuid)) {
                BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(
                        LiveConst.LIVE_PROBLEM.KEY() + stream + ":" + uuid);

                if (!ops.hasKey(stuId.toString())) {
                    ops.put(stuId.toString(), problemDetail);
                    return true;
                }
                return false;
            }

        }catch (Exception e) {
            System.out.println(e);
        }
        return false;
    }

    @Override
    public R getProblemCondition(Integer teaId) {
        //获取直播间的uuid;
        LiveInfo myLive = liveInfoMapper.selectOne(new LambdaQueryWrapper<LiveInfo>().eq(LiveInfo::getLiveBelongTeaId, teaId));

        //获取当前最新的live recordId
        Integer recordId = (Integer) redisTemplate.opsForHash().get(LiveConst.LIVE_CURRENT_OR_LAST.KEY(), LiveConst.LIVE_RECORD_ID.KEY());
        List<Integer> classIds = (List<Integer>) redisTemplate.opsForHash().get(LiveConst.LIVE_CURRENT_OR_LAST.KEY(), LiveConst.LIVE_CLASS_IDS.KEY());

        List<Problem> problems = problemMapper.selectList(new LambdaQueryWrapper<Problem>().eq(Problem::getRecordId, recordId));

        List<Classes> classes = classesService.listByIds(classIds);

        //存放回答情况的map集合 id：回答人数
        HashMap<Integer, Integer> classSignNumberMap = new HashMap<>();

        ArrayList<ProblemRecordVo> problemRecordVos = new ArrayList<>();

        AtomicReference<Integer> totalNumber = new AtomicReference<>(0);

        problems.forEach(problem -> {
            System.out.println(problem);
            totalNumber.updateAndGet(v -> v + problem.getTotalNumber());
            //获取回答者的信息。
            Map<Object, Object> replyInfo =
                    redisTemplate.opsForHash().entries(LiveConst.LIVE_PROBLEM.KEY() + myLive.getLiveUuid() + ":" + problem.getProblemUuid());

            System.out.println(replyInfo);
            ArrayList<ProblemDetail> problemDetails = new ArrayList<>();

            if (replyInfo != null && replyInfo.size() != 0) {

                Set<Object> ids = replyInfo.keySet();

                //找出回答者的详情信息。
                replyInfo.forEach((k, v) -> {
                    ProblemDetail problemDetail = (ProblemDetail) v;
                    if (problemDetail.getPdStuId() != null) {
                        problemDetails.add(problemDetail);
                    }
                });


                //找出特定id的学生来判断所属班级。
                List<Students> students = studentsMapper.selectBatchId(ids);

                if (students.size() != 0) {
                    students.forEach(student -> {
                        //如果学生所属班级已经在map中，那么map中的数据+1
                        if (classSignNumberMap.containsKey(student.getBelongClassId())) {
                            classSignNumberMap.put(student.getBelongClassId(),
                                    classSignNumberMap.get(student.getBelongClassId()) + 1);
                        }else {
                            //如果是第一次存入map,设置初始人数为1
                            classSignNumberMap.put(student.getBelongClassId(), 1);
                        }
                    });

                }


                //筛选每个班级的回答详情
                ArrayList<ProblemClassInfoVo> problemClassInfoVos = new ArrayList<>();
                classes.forEach(c -> {
                    ProblemClassInfoVo problemClassInfoVo = new ProblemClassInfoVo();
                    problemClassInfoVo.setClassId(c.getClassId())
                            .setClassName(c.getClassRealName())
                            .setProblemNumber(classSignNumberMap.get(c.getClassId()));
                    problemClassInfoVos.add(problemClassInfoVo);
                });

                problemRecordVos.add(
                        new ProblemRecordVo().setProblem(problem)
                                .setTotalNumber(totalNumber.get())
                                .setProblemDetails(problemDetails).setClassInfos(problemClassInfoVos)
                );
            }

        });

        return R.success().add("records", problemRecordVos);
    }

    private boolean saveDate2db(String stream, String problemUuid,
                             String problemInfo, Integer totalNumber) {
        //获取 当前开播记录
        Integer recordId = (Integer) redisTemplate.opsForHash().get(LiveConst.LIVE_START_INFO.KEY() + stream,
                LiveConst.LIVE_RECORD_ID.KEY());
        if (recordId == null) return false;

        boolean save = this.save(new Problem().setCreateTime(LocalDateTime.now())
                .setRecordId(recordId)
                .setTotalNumber(totalNumber)
                .setProblemInfo(problemInfo)
                .setProblemUuid(problemUuid));

        //然后修改当前开播记录的 提问问题次数。
        boolean update = liveRecordMapper.increaseProblemNumber(recordId);
        if (save && update) return true;
        return false;
    }
}
